package com.crrc.sandameter.http

import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.net.NetworkInfo
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.cj.mvvmlib.ext.util.GsonInstance
import com.crrc.sandameter.AppContext
import com.crrc.sandameter.common.LogFileUtils
import com.franmontiel.persistentcookiejar.BuildConfig
import com.google.gson.reflect.TypeToken
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONObject
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit


/**
 * 网络请求
 */

private val client =
    OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS).readTimeout(15, TimeUnit.SECONDS)
        .writeTimeout(15, TimeUnit.SECONDS).build()
private val json = "application/json; charset=utf-8".toMediaTypeOrNull()
val handler = Handler(Looper.getMainLooper())
private const val TAG = "http"
const val GET = "GET"
const val POST = "POST"
const val FILE = "POST_FILE"
const val JPEG = "JPEG"
const val IMAGE = ".PNG"
const val MP4 = "video/mp4"

class Http {
    lateinit var url: String
    var body: JSONObject? = null
    var isAutoLogin: Boolean = false //设置是否自动跳登录页
    var requestType: String = "" //请求方式
    var fileType: String = "" //文件类型
    var headers = mutableListOf<Pair<String, String>>()
    var fileParams = mutableListOf<Pair<String, String>>()
    var token: String? = null
    var files: List<File>? = null
    var filesKey: String = ""
    var paramters: MutableMap<String, String> = mutableMapOf()
    var parameterBuilder: FormBody.Builder = FormBody.Builder()
    var multiBody: FormBody? = null
    var file: File? = null
    var success: ((json: String?) -> Unit)? = null
    var fail: ((code: String?, message: String) -> Unit)? = null
    var failWithCode: ((reason: String?, code: Int) -> Unit)? = null
}

fun http(create: Http.() -> Unit) {
    val h = Http().apply { create() }
    when (h.requestType) {
        GET -> httpGet(h)
        POST -> httpParameter(h)
        FILE -> httpMultiParameter(h)
        JPEG -> uploadFiles(h)
        else -> httpPost(h)
    }
}

private fun httpGet(http: Http) {
    if (!isNetConnected()) {
        handler.post { http.fail?.invoke("-1", "网络未连接") }
        return
    }
    val urlBuilder = http.url.toHttpUrlOrNull()?.newBuilder()
    if (http.paramters.isNotEmpty()) {
        http.paramters.forEach {
            urlBuilder?.addQueryParameter(it.key, it.value)
        }
        Log.d(TAG, http.url)
        Log.d(TAG, "请求参数：${http.paramters}")
    }
    urlBuilder?.let { it ->
        val requestBuilder = Request.Builder().url(it.build())
        http.token?.let { requestBuilder.addHeader("X-Access-Token", it) } //添加token
        client.newCall(requestBuilder.build()).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                httpFailure(e, http)
            }

            @Throws(IOException::class)
            override fun onResponse(call: Call, response: Response) {
                httpResponse(response, http)
            }
        })
    }
}

private fun httpPost(http: Http) {
    if (!isNetConnected()) {
        handler.post { http.fail?.invoke("-1", "网络未连接，请检查网络设置！") }
        return
    }
    val jo = http.body ?: ""
    val mediaType = "application/json; charset=utf-8".toMediaTypeOrNull()
    val requestBody = jo.toString().toRequestBody(mediaType)
    LogFileUtils.writeTxtToFile("${http.url}请求参数：${jo}")
    val requestBuilder = Request.Builder().url(http.url).post(requestBody)
    http.token?.let { requestBuilder.addHeader("X-Access-Token", it) } //添加token
    val request = requestBuilder.build()
    Log.d(TAG, "post: ${http.url}\n${request.headers}")
    Log.d(TAG, "post: ${http.url}\n$jo")
    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            httpFailure(e, http)
        }

        @Throws(IOException::class)
        override fun onResponse(call: Call, response: Response) {
            httpResponse(response, http)
        }
    })
}

private fun httpParameter(http: Http, tag: Any = "") {
    if (!isNetConnected()) {
        handler.post { http.fail?.invoke("-1", "网络未连接，请检查网络设置！") }
        return
    }
    val requestBuilder = Request.Builder().url(http.url).post(http.parameterBuilder.build())
    http.token?.let { requestBuilder.addHeader("Authorization", "Bearer $it") } //添加token
    requestBuilder.tag(tag)
    http.headers.let { it ->
        it.forEach {
            requestBuilder.addHeader(it.first, it.second)
        }
    }
    val request = requestBuilder.build()
    if (BuildConfig.DEBUG) {
        val sb = StringBuilder()
        val headers = request.headers
        val names = headers.names()
        sb.append("post: ${http.url}\n")
        sb.append("****headers***\n")

        names.forEachIndexed { index, s ->
            sb.append(s + ":" + headers.value(index) + "\n")
        }
        sb.append("****parameterBody***\n")
        if (request.body is FormBody) {
            val body = request.body as FormBody
            for (i in 0 until body.size) {
                sb.append(body.encodedName(i) + "=" + body.encodedValue(i) + ",")
            }
        }
        Log.i("qdq", sb.toString())
    }
    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            httpFailure(e, http)
        }

        @Throws(IOException::class)
        override fun onResponse(call: Call, response: Response) {
            httpResponse(response, http)
        }
    })
}

private fun httpFailure(e: IOException, http: Http) {
    var msg = e.message ?: "网络错误"
    val exception = e.toString()
    val failJson = GsonInstance.getInstance()?.gson?.toJson(e)
    LogFileUtils.writeTxtToFile("请求地址：${http.url}")
    LogFileUtils.writeTxtToFile("请求错误异常信息：$failJson")
    if ("timeout" in msg.lowercase() || "Timeout" in exception) {
        msg = "请求超时"
    }
    handler.post { http.fail?.invoke("408", msg) }
    Log.d(TAG, http.url)
    Log.d(TAG, "错误信息：$msg")
}

private fun httpResponse(response: Response, http: Http) {
    val result = response.body?.string() ?: ""
    Log.d(TAG, http.url)
    Log.d(TAG, result)
    LogFileUtils.writeTxtToFile("请求地址：${http.url}")
    LogFileUtils.writeTxtToFile("请求结果：${result}")
    val jo: JSONObject
    try {

        jo = JSONObject(result)
    } catch (e: Exception) {
        e.printStackTrace()
        handler.post { http.fail?.invoke("400", "请求错误") }
        return
    }
    when {
        jo.has("code") or jo.has("state") -> {
            val code = if (jo.has("code")) jo.getInt("code") else jo.getInt("state")
            val message = when {
                jo.has("msg") -> jo.getString("msg")
                jo.has("message") -> jo.getString("message")
                else -> ""
            }
            when (code) {
                1 -> {
                    handler.post {
                        try {
                            http.success?.invoke(result)
                        } catch (e: Exception) {
                            println(e)
                            Log.d(TAG, "数据异常")
                        }
                    }
                }

                401 -> {
                    handler.post { http.fail?.invoke(code.toString(), message) }
                }

                else -> {
                    handler.post { http.fail?.invoke(code.toString(), message) }
                }
            }
        }

        jo.has("access_token") -> {
            handler.post {
                try {
                    http.success?.invoke(result)
                } catch (e: Exception) {
                    println(e)
                    Log.d(TAG, "数据异常")
                }
            }
        }

        else -> {
            handler.post { http.fail?.invoke("400", "请求失败：$result") }
        }
    }
}

fun uploadFiles(http: Http) {
    http.file?.let {
        val requestFile: RequestBody = it.asRequestBody("image/jpeg".toMediaTypeOrNull())
        val multipartBody: MultipartBody = MultipartBody.Builder().setType(MultipartBody.FORM)
            .addFormDataPart("file", http.file?.name, requestFile).build()
        val request: Request = Request.Builder().addHeader("X-Access-Token", http.token ?: "")
            .url(http.url) // 替换为你的API URL
            .post(multipartBody).build()
        val okHttpClient = OkHttpClient.Builder().connectTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS).build()
        okHttpClient.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.d(TAG, e.message ?: "")
                httpFailure(e, http)
            }

            @Throws(IOException::class)
            override fun onResponse(call: Call, response: Response) {
                httpResponse(response, http)
            }
        })
    }
}

/**
 * 文件参数混合上传
 */
private fun httpMultiParameter(http: Http) {
    if (!isNetConnected()) {
        handler.post { http.fail?.invoke("-1", "网络未连接，请检查网络设置！") }
        return
    }
    val type =
        if (http.fileType == MP4) MP4.toMediaTypeOrNull() else "image/jpeg".toMediaTypeOrNull()
    val fileBody = (http.file ?: File("")).asRequestBody(type)
    val multipartBody = MultipartBody.Builder().setType(MultipartBody.FORM)
        .addFormDataPart("file", http.file?.name, fileBody)
    if (http.fileParams.size > 0) {
        http.fileParams.forEach {
            multipartBody.addFormDataPart(it.first, it.second)
        }
    }
    val requestBuilder = Request.Builder().url(http.url).post(multipartBody.build())
    http.token?.let { requestBuilder.addHeader("X-Access-Token", it) } //添加token
    http.headers.let { it ->
        it.forEach {
            requestBuilder.addHeader(it.first, it.second)
        }
    }
    val request = requestBuilder.build()
    Log.d(TAG, "post: ${http.url}\n${request.headers}")
    val outTime = if (http.fileType == MP4) 600L else 60L //超时时间
    val okHttpClient = OkHttpClient.Builder().connectTimeout(outTime, TimeUnit.SECONDS)
        .writeTimeout(outTime, TimeUnit.SECONDS).readTimeout(outTime, TimeUnit.SECONDS).build()
    okHttpClient.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            httpFailure(e, http)
        }

        @Throws(IOException::class)
        override fun onResponse(call: Call, response: Response) {
            httpResponse(response, http)
        }
    })
}

/**
 * 检测网络是否连接
 */
fun isNetConnected(): Boolean {
    val cm = AppContext.getContext()
        .getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        val activeNetwork = cm.activeNetwork
        val networkCapabilities: NetworkCapabilities? =
            activeNetwork?.let { cm.getNetworkCapabilities(it) }
        if (networkCapabilities != null && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) {
            return true
        }
    } else {
        val info = cm.allNetworkInfo
        for (anInfo in info) {
            if (anInfo.state == NetworkInfo.State.CONNECTED) {
                Log.d("Network", "NETWORKNAME: " + anInfo.typeName)
                return true
            }
        }
    }

    return false
}

/**
 * 取消网络请求
 */
fun httpParameterCancel(tag: Any) {
    val call = client.dispatcher.queuedCalls().find { it == tag }
    call?.cancel()
}

inline fun <reified T> parseObject(json: String?): T {
    return GsonInstance.getInstance().gson.fromJson(json, object : TypeToken<T>() {}.type)
}